// Ninf-G-XX, ver. 0.1
// Copyright 2011 National Institute of Advanced Industrial Science and Technology
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Expression.hpp"
#include "DataInputStream.hpp"
#include "DataOutputStream.hpp"
#include "Exception.hpp"

using namespace ninf::gen;
using namespace ninf::lib;
/** read a instance from the stream */
Expression :: Expression (DataInputStream & dis) {
  EBEGIN { nEntries = dis.readInt();            } EEND
  entries  = new ExpEntry[nEntries];
  for (int i = 0; i < nEntries; i++){
    EBEGIN { entries[i].tag = (ExpEntryTag) dis.readInt(); } EEND
    EBEGIN { entries[i].val = dis.readInt();                 } EEND
  }
}

/** write a instance to the stream */
void
Expression :: writeTo (DataOutputStream & dos){
  EBEGIN { dos.writeInt(nEntries);   } EEND
  for (int i = 0; i < nEntries; i++){
    EBEGIN { dos.writeInt((int)(entries[i].tag));  } EEND
    EBEGIN { dos.writeInt(      entries[i].val );  } EEND
  }
}


#define PUTTAB(fp,tab)   { for (int i = 0; i < tab; i++) fprintf(fp, "\t");}


/* write out declaration string */
void
Expression :: writeDeclaration(int tab, const char * id, 
				 int index0, int index1, int index2, FILE * fp){
  PUTTAB(fp, tab);
  fprintf(fp, "ExpEntry entries_%s_%d_%d_%d[] = {\n", id, index0, index1, index2);

  PUTTAB(fp, tab + 1);

  int i;
  for (i = 0; i < nEntries - 1; i++){
    entries[i].writeDeclaration(fp);

    fprintf(fp, ", ");
  }
  if (nEntries > 0)
    entries[i].writeDeclaration(fp);
  fprintf(fp, "};\n");

  PUTTAB(fp, tab);
  fprintf(fp, "Expression %s_%d_%d_%d(%d, entries_%s_%d_%d_%d);\n", 
	  id, index0, index1, index2, nEntries, id, index0, index1, index2);
  

}

// evaluate the expression
int  
Expression :: evaluate(int * iArgs, int len, int defaultValue){
  if (nEntries == 0)
    return defaultValue;

  int stack[nEntries];
  int stackPointer = 0;

  for (int i = 0; i < nEntries; i++){
    switch (entries[i].tag){
    case VALUE_CONST:
      // intentional fall down
    case VALUE_IN_ARG:
      stack[stackPointer++] = entries[i].evaluate(iArgs, len);
      break;
    case VALUE_END_OF_OP:
      {
	int v0, v1, v2;

	ExpOp op =  (ExpOp)entries[i].val;
	v0 = stack[--stackPointer];
	switch (op){
	case OP_VALUE_PLUS:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = v1 + v0;
	  break;
	case OP_VALUE_MINUS:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = v1 - v0;
	  break;
	case OP_VALUE_MUL:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = v1 * v0;
	  break;
	case OP_VALUE_DIV:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = v1 / v0;
	  break;
	case OP_VALUE_MOD:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = v1 % v0;
	  break;
	case OP_VALUE_UN_MINUS:
	  stack[stackPointer++] = -v0;
	  break;
	case OP_VALUE_BEKI:
	  v1 = stack[--stackPointer];
	  {
	    int tmp = 1;
	    for (int j = 0; j < v0; j++)
	      tmp *= v1;
	    stack[stackPointer++] = tmp;
	  }
	  break;
	case OP_VALUE_EQ:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = (int)(v1 == v0);
	  break;
	case OP_VALUE_NEQ:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = (int)(v1 != v0);
	  break;
	case OP_VALUE_GT:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = (int)(v1 > v0);
	  break;
	case OP_VALUE_LT:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = (int)(v1 < v0);
	  break;
	case OP_VALUE_GE:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = (int)(v1 >= v0);
	  break;
	case OP_VALUE_LE:
	  v1 = stack[--stackPointer];
	  stack[stackPointer++] = (int)(v1 <= v0);
	  break;
	case OP_VALUE_TRY:
	  v1 = stack[--stackPointer];
	  v2 = stack[--stackPointer];
	  stack[stackPointer++] = v2 ? v1 : v0;
	  break;

	default:
	  EXCEPTION("unknown operator encounterd while evaluating a expression");
	}
      }

    default:
      EXCEPTION("unknown command");
    }

  }
  return stack[--stackPointer];
}

